package com.zlsy.secondtime.singleton;


import java.util.Objects;

/**
 * @author zhouliang
 * @date 2020/6/12 8:39
 */
public class Singleton5 {

    private static volatile Singleton5 INSTANCE = null;

    private Singleton5() {
    }

    public static Singleton5 getInstance() {
        if (Objects.isNull(INSTANCE)) {
            //两个线程同时进来的时候，只有一个线程能进入创建对象。
            //但是虽然能保证只有一个线程进入这个方法，但是不能保证这个INSTANCE = new Singleton5();方法只执行一次
            //因为我们如果两个线程都读取到INSTANCE为null，就会进入synchronized (Singleton5.class)外竞争锁，
            //不管是谁先执行了，第二个线程都会再去执行一次，那么就不能保证是线程安全的了

            //所以，那我们在这个方法里面加个if呐，创建过对象的线程就不创建不就行了？(看似可以)

            synchronized (Singleton5.class) {
                //我们创建对象分为三个步骤；1、新建一个空对象 2、调用构造方法 3、赋值给引用
                // 由于cpu会重排序，所以执行步骤 完全可以是：1、3、2
                //正是由于这步重排序，可以导致有线程可能拿到的是一个构造方法还没初始化的对象

                //比如：线程1竞争到锁，执行1、3步骤，cpu发送资源调度，切换到线程2，线程2进来发现INSTANCE不是null。直接返回这个对象出去
                //但是这个对象的构造函数并没有初始化，只是这个对象不为空了！

                //解决：我们知道volatile可以禁止重排序。所以我们：private static volatile Singleton5 INSTANCE = null;
                //ok 完美解决

                if (Objects.isNull(INSTANCE)) {
                    INSTANCE = new Singleton5();
                }
            }
        }
        return INSTANCE;
    }
}
